#include "QCalendarWidgetEx.h"
#include <QLocale>
#include <QPainter>
#include <QFontDatabase>
#include <QTextCharFormat>
#include <QProxyStyle>
#include <QTableView>
#include <QLayout>
#include <QPushButton>
#include <QHoverEvent>
#include <QCoreApplication>
#include "QTimeWidget.h"

#define kRadiusX 3
#define kRadiusY 3

QCalendarWidgetEx::QCalendarWidgetEx(QWidget *parent)
    : QCalendarWidget(parent)
{
    setStyleSheet(R"(* {font-size: 9pt;} QCalendarWidget QAbstractItemView { border: none; })");
    if (!_showDateTime.isValid())
        _showDateTime = QDateTime::currentDateTime();
    initControl();
    updateDateTime();
}

QCalendarWidgetEx::~QCalendarWidgetEx()
{}

void QCalendarWidgetEx::setDateTime(const QDateTime& dt)
{
    if (!dt.isValid() || _showDateTime == dt) return;
    _showDateTime = dt;
    updateDateTime();
}

void QCalendarWidgetEx::setMinDateTime(const QDateTime& dt)
{
    _minDateTime = dt;
}

void QCalendarWidgetEx::setMaxDateTime(const QDateTime& dt)
{
    _maxDateTime = dt;
}

QDateTime QCalendarWidgetEx::minDateTime()
{
    return _minDateTime;
}

QDateTime QCalendarWidgetEx::maxDateTime()
{
    return _maxDateTime;
}

void QCalendarWidgetEx::slotBtnClicked()
{
    QPushButton *senderBtn = qobject_cast<QPushButton *>(sender());
    senderBtn->setAttribute(Qt::WA_UnderMouse, false);
    QHoverEvent hoverEvent(QEvent::HoverLeave, QPoint(40, 40), QPoint(0, 0));
    QCoreApplication::sendEvent(senderBtn, &hoverEvent);
    if (senderBtn == _hourBtn) {
        showHourPop();
    }
    else if (senderBtn == _minBtn) {
        showMinutePop();
    }
    else if (senderBtn == _yearBtn) {
        showYearPop();
    }
    else if (senderBtn == _monthBtn) {
        showMonthPop();
    }
    else if (senderBtn == _dayBtn) {
        int d = _dayBtn->text().chopped(1).toInt();
        const QDateTime& tmp = _showDateTime;
        QDate tmpDate(tmp.date().year(), tmp.date().month(), d);
        while (!tmpDate.isValid())
            tmpDate.setDate(tmp.date().year(), tmp.date().month(), --d);
        _showDateTime = QDateTime(tmpDate, tmp.time());
        updateDateTime();
    }
}

void QCalendarWidgetEx::slotCalendarClicked(const QDate& date)
{
    if (date != _showDateTime.date()) {
        _showDateTime = QDateTime(date, _showDateTime.time());
        updateDateTime();
    }
}

void QCalendarWidgetEx::slotCalendarActivated(const QDate& date)
{
    _showDateTime.setDate(date);
    emit dateTimeActivated(_showDateTime);
}

void QCalendarWidgetEx::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
{
    if (date == selectedDate())
    {
        painter->save();
        painter->setRenderHint(QPainter::Antialiasing);
        painter->setPen(Qt::NoPen);
        painter->setBrush(QColor(0, 145, 255));
        const QRect& bgRect = rect.adjusted(0, 1, 0, -1);
        painter->drawRoundedRect(bgRect, kRadiusX, kRadiusY);
        painter->setPen(QColor(255, 255, 255));
        painter->drawText(rect, Qt::AlignCenter, QString::number(date.day()));
        painter->restore();
    }
    //else if (date == QDate::currentDate())
    //{
    //    painter->save();
    //    painter->setRenderHint(QPainter::Antialiasing);
    //    painter->setPen(Qt::NoPen);
    //    painter->setBrush(QColor(0, 161, 255));
    //    const QRect& bgRect = rect.adjusted(0, 1, 0, -1);
    //    painter->drawRoundedRect(bgRect, kRadiusX, kRadiusY);
    //    painter->setBrush(QColor(255, 255, 255));
    //    painter->drawRoundedRect(rect.adjusted(1, 2, -1, -2), 2, 2);
    //    painter->setPen(QColor(0, 161, 255));
    //    painter->drawText(rect, Qt::AlignCenter, QString::number(date.day()));
    //    painter->restore();
    //}
    else if (date < minimumDate() || date > maximumDate())
    {
        painter->save();
        painter->setRenderHint(QPainter::Antialiasing);
        painter->setPen(Qt::NoPen);
        painter->setBrush(QColor(249, 249, 249));
        const QRect& bgRect = rect.adjusted(0, 1, 0, -1);
        painter->drawRect(bgRect);
        const auto& dtf = dateTextFormat(date);
        painter->setPen(QColor(220, 220, 220));
        painter->drawText(rect, Qt::AlignCenter, QString::number(date.day()));
        painter->restore();
    }
    else
    {
        QCalendarWidget::paintCell(painter, rect, date);
    }
}

void QCalendarWidgetEx::initControl()
{
    layout()->setSizeConstraint(QLayout::SetFixedSize);
    setLocale(QLocale(QLocale::Chinese));
    setNavigationBarVisible(false);
    setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
    setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames);

    QTextCharFormat format;
    format.setForeground(QColor(30, 30, 30));
    format.setBackground(QColor(255, 255, 255));

    setHeaderTextFormat(format);
    setWeekdayTextFormat(Qt::Saturday, format);
    setWeekdayTextFormat(Qt::Sunday, format);
    setWeekdayTextFormat(Qt::Monday, format);
    setWeekdayTextFormat(Qt::Tuesday, format);
    setWeekdayTextFormat(Qt::Wednesday, format);
    setWeekdayTextFormat(Qt::Thursday, format);
    setWeekdayTextFormat(Qt::Friday, format);

    initTopWidget();
    initBottomWidget();

    connect(this, &QCalendarWidget::activated, this, &QCalendarWidgetEx::slotCalendarActivated);
    connect(this, &QCalendarWidget::clicked, this, &QCalendarWidgetEx::slotCalendarClicked);
}

void QCalendarWidgetEx::initTopWidget()
{
    QWidget* topWidget = new QWidget(this);
    topWidget->setObjectName("CalendarTopWidget");
    topWidget->setFixedHeight(40);
    topWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);

    QHBoxLayout* hboxLayout = new QHBoxLayout;
    hboxLayout->setContentsMargins(9, 0, 9, 0);
    hboxLayout->setSpacing(4);

    _yearBtn = new QPushButton(this);
    _monthBtn = new QPushButton(this);
    _dayBtn = new QPushButton(this);
    _hourBtn = new QPushButton(this);
    _minBtn = new QPushButton(this);

    _yearBtn->setObjectName("CalendarYearBtn");
    _monthBtn->setObjectName("CalendarMonthBtn");
    _dayBtn->setObjectName("CalendarDayBtn");
    _hourBtn->setObjectName("CalendarHourBtn");
    _minBtn->setObjectName("CalendarMinBtn");

    const QString& btnQss = R"(QPushButton{ color: #000000; border-radius: 3px; border: 1px solid #c4c6ca; padding: 4px;}
                               QPushButton:hover { color: #000000; border-color: #0091ff; }
                               QPushButton:pressed { color: #ffffff; background-color: #0091ff; })";
    _yearBtn->setStyleSheet(btnQss);
    _monthBtn->setStyleSheet(btnQss);
    _dayBtn->setStyleSheet(btnQss);
    _hourBtn->setStyleSheet(btnQss);
    _minBtn->setStyleSheet(btnQss);

    hboxLayout->addStretch();
    hboxLayout->addWidget(_yearBtn);
    hboxLayout->addWidget(_monthBtn);
    hboxLayout->addWidget(_dayBtn);
    hboxLayout->addStretch();
    hboxLayout->addWidget(_hourBtn);
    hboxLayout->addWidget(_minBtn);
    hboxLayout->addStretch();
    topWidget->setLayout(hboxLayout);

    connect(_yearBtn, &QPushButton::clicked, this, &QCalendarWidgetEx::slotBtnClicked);
    connect(_monthBtn, &QPushButton::clicked, this, &QCalendarWidgetEx::slotBtnClicked);
    connect(_dayBtn, &QPushButton::clicked, this, &QCalendarWidgetEx::slotBtnClicked);
    connect(_hourBtn, &QPushButton::clicked, this, &QCalendarWidgetEx::slotBtnClicked);
    connect(_minBtn, &QPushButton::clicked, this, &QCalendarWidgetEx::slotBtnClicked);

    QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
    vBodyLayout->insertWidget(0, topWidget);
}

void QCalendarWidgetEx::initBottomWidget()
{
    QWidget* bottomWidget = new QWidget(this);
    bottomWidget->setObjectName("CalendarBottomWidget");
    bottomWidget->setFixedHeight(40);
    bottomWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);

    QHBoxLayout* hboxLayout = new QHBoxLayout;
    hboxLayout->setContentsMargins(12, 0, 12, 0);
    hboxLayout->setSpacing(6);

    _ensureBtn = new QPushButton(this);
    _ensureBtn->setObjectName("CalendarEnsureBtn");
    _ensureBtn->setMinimumWidth(50);
    _ensureBtn->setText("确定");
    const QString& btnQss = R"(QPushButton{ color: #ffffff; background-color: #0091ff; border-radius: 5px; border: 1px solid #0091ff; padding: 4px;}
                               QPushButton:hover { color: #000000; background-color: #ffffff; }
                               QPushButton:pressed { color: #ffffff; background-color: #0091ff; })";
    _ensureBtn->setStyleSheet(btnQss);

    hboxLayout->addStretch();
    hboxLayout->addWidget(_ensureBtn);
    bottomWidget->setLayout(hboxLayout);

    QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
    vBodyLayout->addWidget(bottomWidget);

    connect(_ensureBtn, &QPushButton::clicked, this, [this]() {
        emit dateTimeActivated(_showDateTime);
    });
}

void QCalendarWidgetEx::updateDateTime()
{
    if (_minDateTime.isValid() && _showDateTime < _minDateTime)
        _showDateTime = _minDateTime;
    else if (_maxDateTime.isValid() && _showDateTime > _maxDateTime)
        _showDateTime = _maxDateTime;

    setSelectedDate(_showDateTime.date());
    showSelectedDate();

    _yearBtn->setText(QString::number(_showDateTime.date().year()).rightJustified(4, QLatin1Char('0')).append("年"));
    _monthBtn->setText(QString::number(_showDateTime.date().month()).rightJustified(2, QLatin1Char('0')).append("月"));
    _dayBtn->setText(QString::number(_showDateTime.date().day()).rightJustified(2, QLatin1Char('0')).append("日"));
    _hourBtn->setText(QString::number(_showDateTime.time().hour()).rightJustified(2, QLatin1Char('0')).append("时"));
    _minBtn->setText(QString::number(_showDateTime.time().minute()).rightJustified(2, QLatin1Char('0')).append("分"));
}

void QCalendarWidgetEx::showYearPop()
{
    int minYear = _showDateTime.date().year() - 11;
    if (_minDateTime.isValid())
        minYear = _minDateTime.date().year();
    int maxYear = _showDateTime.date().year() + 12;
    if (_maxDateTime.isValid())
        maxYear = _maxDateTime.date().year();
    QTimeWidget* wgt = new QTimeWidget(4);
    wgt->setWindowFlags(Qt::Popup);
    QList<QString> datas;
    int cnt = qMax(12, 1 + maxYear - minYear);
    for (int i = 0; i < cnt; i++)
        datas.append(QString::number(minYear + i).rightJustified(4, QLatin1Char('0')));
    wgt->setData(datas);
    wgt->setMinValue(minYear);
    wgt->setMaxValue(maxYear);
    wgt->setCurrentValue(_yearBtn->text().chopped(1).toInt());

    const QPoint& gloadPos = _yearBtn->mapToGlobal(QPoint(0, _yearBtn->height()));
    wgt->move(gloadPos);
    connect(wgt, &QTimeWidget::valueChanged, this, [this, wgt](int value) {
        const QDateTime& tmp = _showDateTime;
        int td = tmp.date().day();
        QDate tmpDate(value, tmp.date().month(), td);
        while (!tmpDate.isValid())
            tmpDate = QDate(value, tmp.date().month(), --td);
        _showDateTime = QDateTime(tmpDate, tmp.time());

        updateDateTime();
        wgt->close();
        wgt->deleteLater();
    });
    wgt->show();
}

void QCalendarWidgetEx::showMonthPop()
{
    int min = 1;
    if (_minDateTime.isValid() && _showDateTime.date().year() <= _minDateTime.date().year())
        min = _minDateTime.date().month();
    int max = 12;
    if (_maxDateTime.isValid() && _showDateTime.date().year() >= _maxDateTime.date().year())
        max = _maxDateTime.date().month();
    QTimeWidget* wgt = new QTimeWidget(8);
    wgt->setWindowFlags(Qt::Popup);
    QList<QString> datas;
    for (int i = 1; i <= 12; i++)
        datas.append(QString::number(i).rightJustified(2, QLatin1Char('0')));
    wgt->setData(datas);
    wgt->setMinValue(min);
    wgt->setMaxValue(max);
    wgt->setCurrentValue(_monthBtn->text().chopped(1).toInt());

    int xPos = 0.5 * (this->width() - wgt->sizeHint().width());
    int yPos = _monthBtn->geometry().bottom();
    const QPoint& gloadPos = this->mapToGlobal(QPoint(xPos, yPos));
    wgt->move(gloadPos);
    connect(wgt, &QTimeWidget::valueChanged, this, [this, wgt](int value) {
        const QDateTime& tmp = _showDateTime;
        int td = tmp.date().day();
        QDate tmpDate(tmp.date().year(), value, td);
        while (!tmpDate.isValid())
            tmpDate = QDate(tmp.date().year(), value, --td);
        _showDateTime = QDateTime(tmpDate, tmp.time());
        updateDateTime();
        wgt->close();
        wgt->deleteLater();
    });
    wgt->show();
}

void QCalendarWidgetEx::showHourPop()
{
    int min = 0;
    if (_minDateTime.isValid() && _showDateTime.date() == _minDateTime.date())
        min = _minDateTime.time().hour();
    int max = 23;
    if (_maxDateTime.isValid() && _showDateTime.date() == _maxDateTime.date())
        max = _maxDateTime.time().hour();
    QTimeWidget* wgt = new QTimeWidget(8);
    wgt->setWindowFlags(Qt::Popup);
    QList<QString> datas;
    for (int i = 0; i < 24; i++)
        datas.append(QString::number(i).rightJustified(2, QLatin1Char('0')));
    wgt->setData(datas);
    wgt->setMinValue(min);
    wgt->setMaxValue(max);
    wgt->setCurrentValue(_hourBtn->text().chopped(1).toInt());

    int xPos = 0.5 * (this->width() - wgt->sizeHint().width());
    int yPos = _hourBtn->geometry().bottom();
    const QPoint& gloadPos = this->mapToGlobal(QPoint(xPos, yPos));
    wgt->move(gloadPos);
    connect(wgt, &QTimeWidget::valueChanged, this, [this, wgt](int value) {
        const QDateTime& tmp = _showDateTime;
        _showDateTime = QDateTime(tmp.date(), QTime(value, tmp.time().minute()));
        updateDateTime();
        wgt->close();
        wgt->deleteLater();
    });
    wgt->show();
}

void QCalendarWidgetEx::showMinutePop()
{
    int min = 0;
    if (_minDateTime.isValid() && _showDateTime.date() == _minDateTime.date() && _showDateTime.time().hour() == _minDateTime.time().hour())
        min = _minDateTime.time().minute();
    int max = 59;
    if (_maxDateTime.isValid() && _showDateTime.date() == _maxDateTime.date() && _showDateTime.time().hour() == _maxDateTime.time().hour())
        max = _maxDateTime.time().minute();
    QTimeWidget* wgt = new QTimeWidget(8);
    wgt->setWindowFlags(Qt::Popup);
    QList<QString> datas;
    for (int i = 0; i < 60; i++)
        datas.append(QString::number(i).rightJustified(2, QLatin1Char('0')));
    wgt->setData(datas);
    wgt->setMinValue(min);
    wgt->setMaxValue(max);
    wgt->setCurrentValue(_minBtn->text().chopped(1).toInt());

    int xPos = 0.5 * (width() - wgt->sizeHint().width());
    int yPos = _minBtn->geometry().bottom();
    const QPoint& gloadPos = mapToGlobal(QPoint(xPos, yPos));
    wgt->move(gloadPos);
    connect(wgt, &QTimeWidget::valueChanged, this, [this, wgt](int value) {
        const QDateTime& tmp = _showDateTime;
        _showDateTime = QDateTime(tmp.date(), QTime(tmp.time().hour(), value));
        updateDateTime();
        wgt->close();
        wgt->deleteLater();
    });
    wgt->show();
}

